﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using LibrarySync2.Common.Extensions;
using LibrarySync2.Business.FileSystem.Update;
using LibrarySync2.Business.FileSystem.Item;
using LibrarySync2.Common.Enum;
using LibrarySync2.Common.Interfaces;
using LibrarySync2.Common.Xml;
using System.Xml;
using System.Xml.Serialization;

namespace LibrarySync2.Business.FileSystem
{
    [Serializable]
    public class Library : LibrarySync2.Business.FileSystem.Item.Directory
    {
        public List<IMediaFile> MediaFiles { get; set; }

        public Library()
            : base()
        {
            MediaFiles = new List<IMediaFile>();
            //Do nothing.  Deserializing.
        }

        public Library(DirectoryInfo rootDirectory)
            : base(rootDirectory, rootDirectory.FullName)
        {
            MediaFiles = new List<IMediaFile>();
            LocateMediaFiles(this, MediaFiles);
        }

        //Compare for Black List.. Show We can just see what files are different. No need for partners blacklist here
        public List<IMediaFile> FindDifferences(Library partnersLibrary)
        {
            return CompareMediaFiles(MediaFiles, partnersLibrary);
        }

        public FileSystemUpdate FindDifferences(Library partnersLibrary, Blacklist partnersBlacklist)
        {
            return FindDifferences(partnersLibrary, partnersBlacklist, true);
        }

        public FileSystemUpdate FindDifferences(Library partnersLibrary, Blacklist partnersBlacklist, bool includeParentDirectoryStructure)
        {
            if (partnersLibrary == null)
                throw new ArgumentNullException("partnersLibrary");
            if (partnersBlacklist == null)
                throw new ArgumentNullException("partnersBlacklist");

            var result = new FileSystemUpdate(this.Name, this.FullName, includeParentDirectoryStructure);

            //Locate Media Files
            CompareMediaFiles(this.MediaFiles.FindAll(mf => !partnersBlacklist.ContainsMediaFile(mf.Id)), partnersLibrary, result);

            //Sort
            result.SortList();

            return result;
        }

        #region Protected Methods

        protected static void LocateMediaFiles(IFileSystemItem fileSystemItem, List<IMediaFile> mediaFiles)
        {
            if (fileSystemItem == null)
                throw new ArgumentNullException("fileSystemItem");
            if (mediaFiles == null)
                throw new ArgumentNullException("dictionary");

            var mediaFile = fileSystemItem as IMediaFile;
            if (mediaFile != null && !string.IsNullOrEmpty(mediaFile.Id))
            {
                mediaFiles.Add(mediaFile);
                return;
            }

            fileSystemItem.Children.ForEach(c => LocateMediaFiles(c, mediaFiles));
        }

        protected static void CompareMediaFiles(List<IMediaFile> localFileStore, Library partnersLibrary, IFileSystemUpdate result)
        {
            if (localFileStore == null)
                throw new ArgumentNullException("localFileStore");
            if (partnersLibrary == null)
                throw new ArgumentNullException("partnersLibrary");
            if (result == null)
                throw new ArgumentNullException("result");

            foreach (var file in localFileStore)
            {
                var matchingFile = partnersLibrary.MediaFiles.FirstOrDefault<IMediaFile>(mf => mf.Id.Equals(file.Id, StringComparison.OrdinalIgnoreCase));
                if (matchingFile == null)
                {
                    result.Add(new FileSystemItemUpdate(MediaFileUpdateType.Add, file));
                }
                else if (file.IsBetterThan(matchingFile))
                {
                    result.Add(new FileSystemItemUpdate(MediaFileUpdateType.Replace, file, matchingFile.RelativePath));
                }
            }
        }

        /// <summary>
        /// Copmare for the black list. 
        /// </summary>
        /// <param name="localFileStore"></param>
        /// <param name="partnersLibrary"></param>
        /// <returns></returns>
        protected static List<IMediaFile> CompareMediaFiles(List<IMediaFile> localFileStore, Library partnersLibrary)
        {
            List<IMediaFile> retVal = new List<IMediaFile>(); 
            if (localFileStore == null)
                throw new ArgumentNullException("localFileStore");
            if (partnersLibrary == null)
                throw new ArgumentNullException("partnersLibrary");

            foreach (var file in partnersLibrary.MediaFiles)
            {
                var matchingFile = localFileStore.FirstOrDefault<IMediaFile>(mf => mf.Id.Equals(file.Id, StringComparison.OrdinalIgnoreCase));
                if (matchingFile == null)
                {
                    retVal.Add(file);
                }
            }
            return retVal;
        }

        protected override void ReadElementsFromXml(XmlNode itemNode)
        {
            base.ReadElementsFromXml(itemNode);
            LocateMediaFiles(this, MediaFiles);
        }

        #endregion Protected Methods
    }
}
